package com.crrc.sandameter.device

import java.math.BigInteger

object COMUtil {
    const val DEVICE_ADDRESS = "01" //设备地址
    const val WRITE_COM = "06" //写功能码
    const val READ_COM = "03" //读功能码


    /**
     * 16进制字符串转10进制数字
     */
    fun decodeHEX(hex: String): Long {

        // 1. 将十六进制字符串解析为 BigInteger。
        // 2. 获取其对应的 byte 数组。
        //    这里的关键是，BigInteger 的 toByteArray() 会生成包含符号位的最小字节数组。
        //    对于 "FFFFFFFF" (无符号为 4294967295)，它的 toByteArray() 结果是 [0xFF, 0xFF, 0xFF, 0xFF]
        //    对于 "00000001"，它的 toByteArray() 结果是 [0x01]
        val bigInt = BigInteger(hex, 16)

        // 3. 将这个 BigInteger 强制转换为你期望的特定位宽的有符号类型
        //    如果 `hexs` 始终代表 32 位有符号整数，你可以直接用 `toInt()` 并处理其补码特性
        //    但如果它可能代表 64 位，或者你想模拟标准库的解析，可以这样做：

        // 假设你想要将其解释为 32 位有符号数，最终结果是 Long
        // 这种方式利用了 Kotlin 的 Int 类型的 32 位补码特性
        val intValue = bigInt.toInt() // 0xFFFFFFFF (BigInteger) -> -1 (Int)

        // 然后再转为 Long
        return intValue.toLong()
    }

    /**
     * 计算 Modbus RTU 模式下的 CRC16 校验码。
     *
     * @param data 要计算 CRC 的字节数组（不包含 CRC 本身）。
     * @return 16位 CRC 校验码。
     */
    private fun calculateCrc16(data: ByteArray): Int {
        var crc = 0xFFFF // 预设 CRC 寄存器为 0xFFFF

        for (b in data) {
            crc = crc xor (b.toInt() and 0xFF) // 与数据字节异或

            for (i in 0 until 8) {
                if ((crc and 0x0001) != 0) { // 检查最低有效位
                    crc = crc shr 1 // 右移一位
                    crc = crc xor 0xA001 // 如果 LSB 为 1，与多项式 0xA001 异或
                } else {
                    crc = crc shr 1 // 否则只右移一位
                }
            }
        }
        return crc
    }

    /**
     * 将十六进制字符串转换为字节数组。
     * 例如 "010300000001" 会转换为 [0x01, 0x03, 0x00, 0x00, 0x00, 0x01]
     *
     * @param hexString 十六进制字符串，长度必须是偶数。
     * @return 对应的字节数组。
     */
    fun hexStringToByteArray(hexString: String): ByteArray {
        require(hexString.length % 2 == 0) { "Hex string must have an even number of characters." }
        val len = hexString.length
        val data = ByteArray(len / 2)
        var i = 0
        while (i < len) {
            data[i / 2] =
                ((Character.digit(hexString[i], 16) shl 4) + Character.digit(hexString[i + 1],
                    16)).toByte()
            i += 2
        }
        return data
    }

    /**
     * 将字节数组转换为十六进制字符串。
     * 例如 [0x01, 0x03, 0x84, 0x0A] 会转换为 "0103840A"
     *
     * @param byteArray 字节数组。
     * @return 对应的十六进制字符串。
     */
    fun byteArrayToHexString(byteArray: ByteArray): String {
        return byteArray.joinToString("") { String.format("%02X", it) }
    }

    /**
     * 传入 Modbus 指令的十六进制字符串（不包含 CRC），返回包含 CRC16 的完整指令字符串。
     *
     * @param commandHexWithoutCrc 不包含 CRC 的 Modbus 指令十六进制字符串。
     * 例如: "010300000001"
     * @return 包含 CRC16 的完整 Modbus 指令十六进制字符串。
     */
    fun getFullModbusCommandHexString(commandHexWithoutCrc: String): String {
        val commandBytesWithoutCrc = hexStringToByteArray(commandHexWithoutCrc)
        val crc = calculateCrc16(commandBytesWithoutCrc)

        // CRC16 是低字节在前，高字节在后
        val crcLowByte = (crc and 0xFF).toByte()
        val crcHighByte = (crc shr 8 and 0xFF).toByte()

        // 将 CRC 字节添加到原始字节数组的末尾
        val fullCommandBytes = commandBytesWithoutCrc + crcLowByte + crcHighByte

        // 将完整字节数组转换回十六进制字符串
        return byteArrayToHexString(fullCommandBytes)
    }
}